﻿/*
 * File: GameWindow.cs
 * Developer : Brent Larsen (brentoboy@gmail.com)
 * Date: 2011-AUG-9
 * Copyright (c) 2011 Brent Larsen.
 * Distributed under the terms of the GPL v3
 *  
 * This file is part of GenX-RPG
 * http://code.google.com/p/genx-rpg/ 
 * 
 * GenX-RPG is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * GenX-RPG is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with GenX-RPG.  If not, see <http://www.gnu.org/licenses/>.
 */

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;

namespace Rpg.Game
{
    public partial class GameWindow : Form
    {
        public GameWindow()
        {
            InitializeComponent();
        }

        public GameWindow(string dummy)
            :this()
        {
            // now load the list of adventures to choose from.
            foreach (var kvp in AdventureDatabase.GetAdventureList())
            {
                var menuItem = new ToolStripMenuItem();
                menuItem.Name = "menuFile_Open_{0}".f(kvp.Key.Replace(".", "_"));
                menuItem.Text = "{0} ({1})".f(kvp.Value, kvp.Key);
                menuItem.Tag = kvp.Key;
                menuItem.Click += new EventHandler(menuFile_Open_Click);
                this.menuFile_LoadAdventure.DropDownItems.Insert(0, menuItem);
            }
        }

        private void menuFile_Open_Click(object sender, EventArgs e)
        {
            var menuItemAdventure = sender as ToolStripMenuItem;
            _loadAdventure(menuItemAdventure.Tag.ToString());
        }

        private void _loadAdventure(string dbPath)
        {
            _db = new AdventureDatabase(dbPath);

            // update the caption for the window
            this.Text = "GenX RPG - {0}".f(_db.GetAdventureName());

            // throw out maps that were loaded previously
            var savedGames = this.menuSavedGame.DropDownItems.OfType<ToolStripMenuItem>().Where(m => m.Tag != null).ToList();
            foreach (var game in savedGames)
            {
                this.menuSavedGame.DropDownItems.Remove(game);
            }

            // add maps for this database
            foreach (var game in _db.GetSavedGameNames())
            {
                var menuItem = new ToolStripMenuItem();
                menuItem.Name = "menuSaveGame_Open{0}".f(game.Key);
                menuItem.Text = "Load Game: " + game.Value;
                menuItem.Tag = game.Key;
                menuItem.Click += new EventHandler(menuSavedGame_Open_Click);
                this.menuSavedGame.DropDownItems.Add(menuItem);
            }

            menuSavedGame_Close.Enabled = false;
            menuSavedGame_StartNew.Enabled = true;
        }

        private void menuFileExit_Click(object sender, EventArgs e)
        {
            this.Close();
        }

        private void menuSavedGame_StartNew_Click(object sender, EventArgs e)
        {
            var id = _db.StartGame("New Game");
            new SavedGameActionWindow(id).Show();
        }

        private void menuSavedGame_Open_Click(object sender, EventArgs e)
        {
            var menuItem = sender as ToolStripMenuItem;
            new SavedGameActionWindow(menuItem.Tag.AsInt().Value).Show();
        }

        private void menuSavedGame_Close_Click(object sender, EventArgs e)
        {
            _gameOff();
        }

        private AdventureDatabase _db { get { return Program.Db; } set { Program.Db = value; } }
        private int _savedGameId;
        private Location _location = new Location();
        private Map _map;
        public Map CurrentMap { get { return _map; } }
        public int CurrentX { get { return _location.X; } }
        public int CurrentY { get { return _location.Y; } }
        private System.Timers.Timer _timer;
        private int _tickIndex = 0;

        private Dictionary<Keys, bool> _downKeys = new Dictionary<Keys, bool>() 
        {
            { Keys.Left, false} ,
            { Keys.Right, false },
            { Keys.Up, false },
            { Keys.Down, false },
        };

        public void _gameOn()
        {
            foreach (var menuitem in menuSavedGame.DropDownItems.OfType<ToolStripMenuItem>())
            {
                menuitem.Enabled = false;
            }
            foreach (var menuitem in menuFile_LoadAdventure.DropDownItems.OfType<ToolStripMenuItem>())
            {
                menuitem.Enabled = false;
            }
            menuSavedGame_Close.Enabled = true;

            _timer = new System.Timers.Timer(1000d / 16);
            _timer.Elapsed += (s, e) => { this.Tick(); };
            _timer.Stop();
        }

        public void _gameOff()
        {
            _timer.Stop();
            _timer.Dispose();
            _timer = null;

            _map = null;
            _savedGameId = 0;

            foreach (var menuitem in menuSavedGame.DropDownItems.OfType<ToolStripMenuItem>())
            {
                menuitem.Enabled = true;
            }
            foreach (var menuitem in menuFile_LoadAdventure.DropDownItems.OfType<ToolStripMenuItem>())
            {
                menuitem.Enabled = true;
            }
            menuSavedGame_Close.Enabled = false;
        }

        public void OpenSavedGame(int gameId)
        {
            _savedGameId = gameId;
            var startLocation = _db.GetCurrentLocation(gameId);

            _gameOn();
            SetCurrentLocation(startLocation.MapId, startLocation.X, startLocation.Y);
        }

        public void CloseSavedGame()
        {
            _gameOff();
        }

        public void SetCurrentLocation(int mapID, int x, int y)
        {
            _timer.Stop();
            _map = _db.LoadMap(mapID, _savedGameId);
            SetCurrentLocation(x, y);
            MapCanvas.Refresh();
            _timer.Start();
        }

        public void SetCurrentLocation(int x, int y)
        {
            var portal = _map.Portals.FirstOrDefault(p => p.X1 <= x && p.X2 >= x && p.Y1 <= y && p.Y2 >= y);
            if (portal == null)
            {
                _location.X = x;
                _location.Y = y;
                _db.SetCurrentLocation(_savedGameId, _map.Id, x, y);
                MapCanvas.SetOrigin(this.CurrentX, this.CurrentY);
            }
            else
            {
                var oldX = _location.X; // the squair they came from
                var newX = x; // the squair they are stepping on which contains the portal

                var oldY = _location.Y; // the squair they came from
                var newY = y; // the squair they are stepping on which contains the portal

                int toX = ((portal.To_X1 + portal.To_X2) / 2);
                int toY = ((portal.To_Y1 + portal.To_Y2) / 2);

                if (portal.To_X1 == portal.To_X2) // not much of a range to choose from
                {
                    toX = portal.To_X1;
                }
                else if (oldX < newX && newX == portal.X1) // they are comming from the left
                { // start them at the left most part of the other portal
                    toX = portal.To_X1;
                }
                else if (oldX > newX && newX == portal.X2) // they are comming from the right
                { // start them at the right most part of the other portal
                    toX = portal.To_X2;
                }
                else
                { // put them someplace reasonably close to average for where they came from
                    var howFarFromX1 = x - portal.X1;
                    var howFarFromX2 = portal.X2 - x;
                    var ratioX = ((portal.X2 - (decimal)portal.X1) + 1) / ((portal.To_X2 - (decimal)portal.To_X1) + 1);
                    toX = (int)decimal.Round((((portal.To_X1 + (howFarFromX1 * ratioX)) + (portal.To_X2 - (howFarFromX2 * ratioX))) / 2), 0);
                }

                if (portal.To_Y1 == portal.To_Y2) // not much of a range to choose from
                {
                    toY = portal.To_Y1;
                }
                else if (oldY < newY && newY == portal.Y1) // they are comming from above
                { // start them at the topmost part of the other portal
                    toY = portal.To_Y1;
                }
                else if (oldY > newY && newY == portal.Y2) // they are comming from the right
                { // start them at the right most part of the other portal
                    toY = portal.To_Y2;
                }
                else
                { // put them someplace reasonably close to average for where they came from
                    var howFarFromY1 = y - portal.Y1;
                    var howFarFromY2 = portal.Y2 - y;
                    var ratioY = ((portal.Y2 - (decimal)portal.Y1) + 1) / ((portal.To_Y2 - (decimal)portal.To_Y1) + 1);
                    toY = decimal.Round((((portal.To_Y1 + (howFarFromY1 * ratioY)) + (portal.To_X2 - (howFarFromY2 * ratioY))) / 2), 0).AsInt().Value;
                }

                Contract.Asserts(portal.To_X1 <= toX && toX <= portal.To_X2);
                Contract.Asserts(portal.To_Y1 <= toY && toY <= portal.To_Y2);

                Program.Db.SetPortalState(_savedGameId, portal.Id, (int)ElementStates.BeenThereDoneThat);
                SetCurrentLocation(portal.To_MapId, toX, toY);
            }
        }


        int _upPressed = 0;
        int _downPressed = 0;
        int _leftPressed = 0;
        int _rightPressed = 0;

        public void Tick()
        {
            _timer.Stop();
            _tickIndex++;

            lock (_downKeys)
            {

                if (_tickIndex % 2 == 0) //movement
                {
                    var x = this.CurrentX;
                    var y = this.CurrentY;

                    if (_upPressed > 0 || _downKeys[Keys.Up])
                    {
                        if ((y > 0)
                            && ((_map.Cells[x, y].Walk & Tile.BLOCK_NORTH) == 0)
                            && ((_map.Cells[x, y - 1].Walk & Tile.BLOCK_SOUTH) == 0))
                        {
                            y--;
                        }
                        if (_upPressed > 0) _upPressed--;
                    }

                    if (_downPressed > 0 || _downKeys[Keys.Down])
                    {
                        if ((y < _map.Height - 1)
                            && ((_map.Cells[x, y].Walk & Tile.BLOCK_SOUTH) == 0)
                            && ((_map.Cells[x, y + 1].Walk & Tile.BLOCK_NORTH) == 0))
                        {
                            y++;
                        }
                        if (_downPressed > 0) _downPressed--;
                    }

                    if (_leftPressed > 0 || _downKeys[Keys.Left])
                    {
                        if ((x > 0)
                            && ((_map.Cells[x, y].Walk & Tile.BLOCK_WEST) == 0)
                            && ((_map.Cells[x - 1, y].Walk & Tile.BLOCK_EAST) == 0))
                        {
                            x--;
                        }
                        if (_leftPressed > 0) _leftPressed--;
                    }

                    if (_rightPressed > 0 || _downKeys[Keys.Right])
                    {
                        if ((x < _map.Width - 1) 
                            && ((_map.Cells[x, y].Walk & Tile.BLOCK_EAST) == 0)
                            && ((_map.Cells[x + 1, y].Walk & Tile.BLOCK_WEST) == 0))
                        {
                            x++;
                        }
                        if (_rightPressed > 0) _rightPressed--;
                    }

                    if (x != this.CurrentX || y != this.CurrentY)
                    {
                        this.SetCurrentLocation(x, y);
                    }
                }
                _timer.Start();
            }
        }

        protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
        {
            const int WM_KEYDOWN = 0x100;
            const int WM_SYSKEYDOWN = 0x104;

            if ((msg.Msg == WM_KEYDOWN) || (msg.Msg == WM_SYSKEYDOWN))
            {
                switch(keyData)
                {
                    case Keys.Down:
                    case Keys.Up:
                    case Keys.Left:
                    case Keys.Right:
                        this.Form_KeyDown(this, new KeyEventArgs(keyData));
                        break;

                    //case Keys.Tab:
                    //    this.Parent.Text="Tab Key Captured";
                    //    break;
 
                    //case Keys.Control | Keys.M:
                    //    this.Parent.Text="<CTRL> + M Captured";
                    //    break;
 
                    //case Keys.Alt | Keys.Z:
                    //    this.Parent.Text="<ALT> + Z Captured";
                    //    break;
                }				
            }

            return base.ProcessCmdKey(ref msg,keyData);
        }

        private void Form_KeyDown(object sender, KeyEventArgs e)
        {
            lock (_downKeys)
            {
                if (!this._downKeys.ContainsKey(e.KeyCode))
                {
                    this._downKeys.Add(e.KeyCode, false);
                }

                if (this._downKeys[e.KeyCode] == true)
                {
                    e.SuppressKeyPress = true;
                }
                else
                {
                    this._downKeys[e.KeyCode] = true;

                    switch (e.KeyCode)
                    {
                        case Keys.Up:
                            if (_downPressed > 0)
                            {
                                _downPressed = 0;
                                _upPressed = 1;
                            }
                            else
                            {
                                _upPressed++;
                            }
                            break;
                        case Keys.Down:
                            if (_upPressed > 0)
                            {
                                _upPressed = 0;
                                _downPressed = 1;
                            }
                            else
                            {
                                _downPressed++;
                            }
                            break;
                        case Keys.Left:
                            if (_rightPressed > 0)
                            {
                                _rightPressed = 0;
                                _leftPressed = 1;
                            }
                            else
                            {
                                _leftPressed++;
                            }
                            break;
                        case Keys.Right:
                            if (_leftPressed > 0)
                            {
                                _leftPressed = 0;
                                _rightPressed = 1;
                            }
                            else
                            {
                                _rightPressed++;
                            }
                            break;
                        default: // not a big deal
                            break;
                    }
                }
            }
        }

        private void Form_KeyUp(object sender, KeyEventArgs e)
        {
            lock (_downKeys)
            {
                if (!this._downKeys.ContainsKey(e.KeyCode))
                {
                    this._downKeys.Add(e.KeyCode, false);
                }
                else
                {
                    this._downKeys[e.KeyCode] = false;
                }
            }
        }
    }
}
